package cn.hanyx.vlog.articleservice.interceptor;

import cn.hanyx.vlog.articleservice.annotation.JwtToken;
import cn.hanyx.vlog.articleservice.config.AppConfig;
import cn.hanyx.vlog.articleservice.dto.ResponseObject;
import cn.hanyx.vlog.articleservice.entity.User;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import sun.misc.BASE64Encoder;

import javax.annotation.Resource;
import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * TokenInterceptor
 * 对存放在请求头中的jwt进行校验，通过执行目标方法，失败提示错误
 *
 * @author CSS
 * @date 2021/11/10
 */
public class TokenInterceptor implements HandlerInterceptor {

    @Resource
    private AppConfig appConfig;

    /**
     * 在目标方法执行前先执行preHandle进行前置处理
     *
     * @param request  原生请求对象
     * @param response 原生响应对象
     * @param handler  处理器对象
     * @return true-请求向后送达到Controller, false-中断请求立即产生响应
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("TokenInterceptor.preHandle()");
        String appKey = appConfig.getAppKey();
        System.out.println("appKey:" + appKey);
        // 如果映射的不是方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        response.setContentType("text/json;charset=utf-8");
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //获取目标方法的Method对象
        Method method = handlerMethod.getMethod();
        //判断目标方法是否包含JwtToken注解
        if (method.isAnnotationPresent(JwtToken.class)) {
            String token = request.getHeader("token");
            //判断请求头是否包含token属性,在拥有@JwtToken的方法处理时,
            // requird == true && token == null 时抛出异常
            if (token == null) {
                JwtToken jwtToken = method.getAnnotation(JwtToken.class);
                if (jwtToken.requird() == true) {
                    //未认证
                    response.setStatus(401);
                    ResponseObject<Object> responseObject = new ResponseObject<>("SecurityException", "token不存在，请检查请求头是否包含token。");
                    String json = objectMapper.writeValueAsString(responseObject);
                    response.getWriter().println(json);
                    return false;
                }
            } else { //token存在时验证JWT的有效性
                //1.对秘钥做BASE64编码
                String base64 = new BASE64Encoder().encode(appKey.getBytes());
                //2.生成秘钥对象,会根据base64长度自动选择相应的 HMAC 算法
                SecretKey key = Keys.hmacShaKeyFor(base64.getBytes());
                //3.验证Token
                try {
                    JwtParser parser = Jwts.parserBuilder().setSigningKey(key).build();
                    Jws<Claims> claimsJws = parser.parseClaimsJws(token);
                    String userJson = claimsJws.getBody().getSubject();
                    System.out.println(userJson);
                    User user = objectMapper.readValue(userJson, User.class);
                    // 把用户对象添加到请求中。
                    request.setAttribute("$user", user);
                    return true;
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                    response.setStatus(500);
                    ResponseObject responseObject = new ResponseObject(e.getClass().getSimpleName(), e.getMessage());
                    String json = objectMapper.writeValueAsString(responseObject);
                    response.getWriter().println(json);
                    return false;
                } catch (JwtException e) {
                    e.printStackTrace();
                    response.setStatus(401);
                    ResponseObject responseObject = new ResponseObject(e.getClass().getSimpleName(), e.getMessage());
                    String json = objectMapper.writeValueAsString(responseObject);
                    response.getWriter().println(json);
                    return false;
                }
            }
        }
        return true;
    }
}
